package org.sja.log2traffic;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.Period;
import org.joda.time.PeriodType;
import org.sja.log2traffic.dataStructures.ChartPeriodEnum;
import org.sja.log2traffic.dataStructures.ConfigurationEntity;
import org.sja.log2traffic.dataStructures.CounterForDateAndResponseTimeEntity;
import org.sja.log2traffic.dataStructures.DateAndResponseTimeEntity;
import org.sja.log2traffic.dataStructures.INewLogInfo;
import org.sja.log2traffic.dataStructures.ResultColumn;
import org.sja.log2traffic.dataStructures.ResultTable;
import org.sja.log2traffic.dataStructures.ResultValue;
import org.sja.log2traffic.dataStructures.simpleTime.LogDataSimpleTime;
import org.sja.log2traffic.dataStructures.timeAgregator.LogDataTimeAgregator;


public class DataCalculator implements ITimeAgregatorCalculator, ISimpleTimeCalculator, IDateTimeFormatter, IStatisticCalculator {

	public SimpleDateFormat DATE_FORMAT_WITH_MILIS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
	public SimpleDateFormat DATE_FORMAT_WITH_MINUTES = new SimpleDateFormat("yyyy-MM-dd HH:mm");
	public SimpleDateFormat DATE_FORMAT_CONDENSED = new SimpleDateFormat("yyyyMMdd");

	@Override
	public INewLogInfo getLogInfo(LogDataTimeAgregator logData, long time) {
		
		if (time < logData.getFrom()) {
			return null;
		}
		if (time >= logData.getTo()) {
			return null;
		}

		return getMajorMinorForTime(logData, time);
	}

	public Long getDateForIndex(LogDataTimeAgregator logData, int major, int minor) {
		DateTime dateTime = new DateTime(logData.getFrom());
		if (logData.getChartPeriodEnum() == ChartPeriodEnum.DAY){
			return dateTime.plusDays(major).plusMinutes(minor).getMillis();
		} else if (logData.getChartPeriodEnum() == ChartPeriodEnum.HOUR){
			return dateTime.plusHours(major).plusMinutes(minor).getMillis();			
		}
		return 0L;
	}

	public Long getDateForIndex(LogDataTimeAgregator logData, int major) {
		DateTime dateTime = new DateTime(logData.getFrom());
		if (logData.getChartPeriodEnum() == ChartPeriodEnum.DAY){
			return dateTime.plusDays(major).getMillis();
		} else if (logData.getChartPeriodEnum() == ChartPeriodEnum.HOUR){
			return dateTime.plusHours(major).getMillis();			
		}
		return 0L;
	}

	public INewLogInfo getMajorMinorForTime(LogDataTimeAgregator logData, long time) {
		
		int major = -1;
		int minor = -1;
		if (logData.getChartPeriodEnum() == ChartPeriodEnum.DAY){
			Period periodDays = new Interval(logData.getFrom(),time).toPeriod(PeriodType.dayTime());
			major = periodDays.getDays();
			minor = periodDays.getHours()*60 + periodDays.getMinutes();
		} else if (logData.getChartPeriodEnum() == ChartPeriodEnum.HOUR){
			Period periodHours = new Interval(logData.getFrom(),time).toPeriod(PeriodType.time());
			major = periodHours.getHours();
			minor = periodHours.getMinutes();
		}
		return logData.getRawData(major, minor);

	}

	@Override
	public String longToStringWithMilis(long time) {
		return DATE_FORMAT_WITH_MILIS.format(new Date(time));
	}

	@Override
	public String longToStringWithMinutes(long time) {
		return DATE_FORMAT_WITH_MINUTES.format(new Date(time));
	}

	@Override
	public String longToStringCondendensed(long time) {
		return DATE_FORMAT_CONDENSED.format(new Date(time));
	}

	@Override
	public Long stringWithMilisToLong(String date) {
		try {
			return DATE_FORMAT_WITH_MILIS.parse(date).getTime();
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Long stringWithMinutesToLong(String date) {
		try {
			return DATE_FORMAT_WITH_MINUTES.parse(date).getTime();
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Long stringCondensedToLong(String date) {
		try {
			return DATE_FORMAT_CONDENSED.parse(date).getTime();
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}

	public Long addHour(long time, long amount) {
		return time + (amount * 60 * 60 * 1000);
	}

	public Long addMiniute(long time, long amount) {
		return time + (amount * 60 * 1000);
	}

	@Override
	public ResultTable calculateTableForAgregate(ConfigurationEntity configurationFromTo, LogDataTimeAgregator logDataTimeAgregator, String category) {

		ResultTable result = new ResultTable(logDataTimeAgregator.getColumnDesciptors(configurationFromTo.getChartPeriodEnum()), logDataTimeAgregator.getAmountOfMajors());
		for (int i = 0; i < logDataTimeAgregator.getAmountOfMajors(); i++) {
			int maxCount = 0;
			Long maxRespTime = 0L;
			long respTimeSum = 0;
			long amountforAvg = 0;
			for (int j = 0; j < logDataTimeAgregator.getAmountOfMinors(); j++) {
				CounterForDateAndResponseTimeEntity rawDataElement = logDataTimeAgregator.getRawData(i, j).getRawDataElementForCategory(category);
				if (rawDataElement != null) {
					Long currentRespTime = rawDataElement.getMaxDateAndTime().getResponseTime();
					int currentCount = rawDataElement.getCount();
					if (currentCount > maxCount) {
						maxCount = currentCount;
					}

					if (currentRespTime > maxRespTime) {
						maxRespTime = currentRespTime;
					}
					respTimeSum += currentRespTime;
					amountforAvg += currentCount;
				}
			}
			Long dateAsLong = getDateForIndex(logDataTimeAgregator, i);
			String date = longToStringWithMilis(dateAsLong);
			ResultValue resultValue0 = new ResultValue(date, dateAsLong);

			if (amountforAvg > 0) {

				Long avgRespTime = (Long) (respTimeSum / amountforAvg);

				ResultValue resultValue1 = new ResultValue(String.valueOf(maxCount), (long)maxCount);
				ResultValue resultValue2 = new ResultValue(String.valueOf(amountforAvg), amountforAvg);
				ResultValue resultValue3 = new ResultValue(maxRespTime.toString(), maxRespTime);
				ResultValue resultValue4 = new ResultValue(avgRespTime.toString(), avgRespTime);

				ResultValue[] resultValues = new ResultValue[] { resultValue0, resultValue1, resultValue2, resultValue3, resultValue4 };
				result.getValues().add(resultValues);

			} else {

				ResultValue zeroValue = new ResultValue("0", 0L);

				ResultValue[] resultValues = new ResultValue[] { resultValue0, zeroValue, zeroValue, zeroValue, zeroValue };
				result.getValues().add(resultValues);

			}

		}

		return result;

	}

	@Override
	public ResultTable getTableForSimple(LogDataSimpleTime logDataSimpleTime, String category) {

		int rawDataAmount = logDataSimpleTime.getRawDataAmount();
		ResultTable result = new ResultTable(logDataSimpleTime.getColumnDesciptors(), logDataSimpleTime.getRawDataAmount());
		DateTime base = new DateTime(logDataSimpleTime.getFrom());
		for (int i = 0; i < rawDataAmount; i++) {
			CounterForDateAndResponseTimeEntity counterForDateAndResponseTime = logDataSimpleTime.getRawDataElement(i).getRawDataElementForCategory(category);
			long valueAsLong = base.plusMinutes(i).getMillis();
			ResultValue zeroValue = new ResultValue("0", 0L);
			ResultValue resultValue0 = new ResultValue(longToStringWithMilis(valueAsLong), valueAsLong);
			if (counterForDateAndResponseTime != null) {
				int count = counterForDateAndResponseTime.getCount();
				DateAndResponseTimeEntity maxDateAndTime = counterForDateAndResponseTime.getMaxDateAndTime();
				ResultValue resultValue1 = new ResultValue(maxDateAndTime.getResponseTime().toString(), maxDateAndTime.getResponseTime());
				ResultValue resultValue2 = new ResultValue(String.valueOf(count), (long)count);
				ResultValue[] resultValues = new ResultValue[] { resultValue0, resultValue1, resultValue2 };
				result.getValues().add(resultValues);
			} else {
				ResultValue[] resultValues = new ResultValue[] { resultValue0, zeroValue, zeroValue };
				result.getValues().add(resultValues);				
			}
		}
		return result;
	}

	@Override
	public ResultTable getTableStatistic(CounterForDateAndResponseTimeEntity counterForDateAndResponseTimeEntity) {

		int omega = counterForDateAndResponseTimeEntity.getCount();
		int amountOfElements = (int)(counterForDateAndResponseTimeEntity.getMaxDateAndTime().getResponseTime() / 100);
				
		ResultTable result = new ResultTable(new ResultColumn[]{new ResultColumn("response time"), new ResultColumn("probability"), new ResultColumn("the distribution")}, amountOfElements);
		double distribution = 0.0; 
		for (int i=0; i<amountOfElements; i++){
			ResultValue resultValue0 = new ResultValue(String.valueOf(i*100L), i*100L);
			Integer probabilityVariable =  counterForDateAndResponseTimeEntity.getRespTimeDistribution().get(i);
			if (probabilityVariable == null){
				probabilityVariable = 0;
			}
			double probability = ((double)probabilityVariable / omega);
			distribution += probability;
			ResultValue resultValue1 = new ResultValue(String.valueOf(probability), probability);
			ResultValue resultValue2 = new ResultValue(String.valueOf(distribution), distribution);
			result.getValues().add(new ResultValue[]{resultValue0,resultValue1,resultValue2});
		}
		
		return result;
		
	}
	
	
}
